package persistencia;

import excepciones.PersistenciaException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;


public class PoolTransacciones {
	//Instancia unica del pool de conexiones
	private static PoolTransacciones instance;
	// url, user, password de la BD utilizadas para abrir las nuevas conexiones.
	private String driver, url, user, password;
	private int nivelTransaccionalidad;
	//Indicadores de la cantidad de conexiones creadas y cantidad maxima de conexiones permitidas.
	private int tamanio, creadas, tope;
	// Conexiones disponibles para ser utilizadas
	private ArrayList<Connection> arreglo;

	// Constructor privado del conjunto de transacciones.
	private PoolTransacciones() throws PersistenciaException {
		arreglo = new ArrayList<Connection>();
		creadas = 0;
		tope = 0;
			
		LectorParametros lector;
		try {
			lector = new LectorParametros("datos.txt");
			url = lector.getUrlBase();
			user = lector.getUsuarioBase();
			password = lector.getContraseniaBase();
			driver = lector.getDriverBase();
			tamanio = lector.getCantConexiones();
			Class.forName(driver).newInstance();
			tope = 0;
			nivelTransaccionalidad = lector.getNivelTransaccionalidad();
		} catch (PersistenciaException e) {
			throw new PersistenciaException(e.getMensaje());
		} catch (InstantiationException e) {
			throw new PersistenciaException(e.getMessage());
		} catch (IllegalAccessException e) {
			throw new PersistenciaException(e.getMessage());
		} catch (ClassNotFoundException e) {
			throw new PersistenciaException(e.getMessage());
		} 
	}
	// Metodo encargado de generar las instancias del manejador de transacciones
	public static PoolTransacciones getInstance() throws PersistenciaException {
		if (instance == null)
			instance = new PoolTransacciones();
		return instance;
	}

	// Crea una nueva transaccion, si no hay conexiones libres se bloqueara
	// hasta que alguna se libere. El mismo se encarga de obtener la transaccion
	// seteando apropidadamente el nivel de transaccionalidad.
	public synchronized Connection obtenerTransaccion()
			throws PersistenciaException {
		Connection con;
	try{
		if (creadas == tamanio) {
			wait();
		}		
		if (tope > 0) {
			con = arreglo.get(arreglo.size() - 1);
			arreglo.remove(arreglo.size() - 1);
			tope--;
		} else{
			con = DriverManager.getConnection(url, user, password);			
			
		}
		con.setAutoCommit(false);
		con.setTransactionIsolation(nivelTransaccionalidad);	
		
		creadas++;
	} catch(InterruptedException e)
	{
		throw new PersistenciaException(e.getMessage());
	} catch (SQLException e) {
		throw new PersistenciaException(e.getMessage());
	}
	
		return con;
	}
	
	/**
	 * Libera la transaccion para ser reutilizada por el conjunto de
	 * transacciones.
	 * 
	 * @param transaccion
	 *            Transaccion a liberar.
	 */
	public synchronized void finalizarTransaccion(Connection con, boolean ok)
			throws PersistenciaException {
		try{
		if (ok)
			con.commit();
		else
			con.rollback();

		creadas--;
		arreglo.add(con);
		tope++;

		notify();
		} catch(SQLException e)
		{
			throw new PersistenciaException(e.getMessage());
		}
	}
}